/*

 * Copyright (C) 2021 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */
package com.headerfooter.songhang.library.component;

import ohos.agp.components.*;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

import static com.headerfooter.songhang.library.component.Recycler.DIRECTION_UP;
import static com.headerfooter.songhang.library.component.Recycler.DIRECTION_DOWN;

/**
 * 自定义瀑布流布局
 */
public class SpanLayout extends ComponentContainer implements ComponentContainer.EstimateSizeListener,
        ComponentContainer.ArrangeListener, Component.CanAcceptScrollListener, Component.ScrolledListener,
        Component.TouchEventListener, Component.DraggedListener {
    private Recycler mRecycler;
    private SpanLayoutManager mLayoutManager;
    private int mColumnNum = 2;

    private int startY = 0;
    private int scrollY = 0;
    private int mDirection = DIRECTION_UP;
    private boolean mChange = false;

    private BaseItemProvider mProvider;

    /**
     * 构造方法
     *
     * @param context Context对象
     */
    public SpanLayout(Context context) {
        super(context);
        setEstimateSizeListener(this);
        setArrangeListener(this);
        setCanAcceptScrollListener(this);
        setScrolledListener(this);
        setTouchEventListener(this);
        setDraggedListener(DRAG_VERTICAL, this);
        setAutoLayout(true);
    }

    /**
     * 构造方法
     *
     * @param context Context对象
     * @param attrSet 属性
     */
    public SpanLayout(Context context, AttrSet attrSet) {
        super(context, attrSet);
    }

    /**
     * 构造方法
     *
     * @param context Context对象
     * @param attrSet 属性
     * @param styleName 样式名称
     */
    public SpanLayout(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
    }

    /**
     * 设置provider
     *
     * @param provider BaseItemProvider对象
     */
    public void setItemProvider(BaseItemProvider provider) {
        mProvider = provider;
        mRecycler = new Recycler(this, mProvider);
        if (mLayoutManager != null) {
            mLayoutManager.setProvider(mProvider);
            mLayoutManager.setRecycler(mRecycler);
        }
    }

    /**
     * 设置布局管理
     *
     * @param manager SpanLayoutManager对象
     */
    public void setLayoutManager(SpanLayoutManager manager) {
        mLayoutManager = manager;
        mLayoutManager.setProvider(mProvider);
        mLayoutManager.setRecycler(mRecycler);
        mLayoutManager.setSpanLayout(this);
    }

    /**
     * 获取provider
     *
     * @return BaseItemProvider
     */
    public BaseItemProvider getItemProvider() {
        return mProvider;
    }

    /**
     * 设置列数
     *
     * @param columnNum 列数
     */
    public void setColumnNum(int columnNum) {
        mColumnNum = columnNum;
    }

    @Override
    public boolean onEstimateSize(int widthEstimatedConfig, int heightEstimatedConfig) {
        int width = EstimateSpec.getSize(widthEstimatedConfig);
        int height = EstimateSpec.getSize(heightEstimatedConfig);
        setEstimatedSize(
                EstimateSpec.getChildSizeWithMode(width, widthEstimatedConfig, EstimateSpec.UNCONSTRAINT),
                EstimateSpec.getChildSizeWithMode(height, heightEstimatedConfig, EstimateSpec.UNCONSTRAINT));
        mLayoutManager.setEstimateSize(widthEstimatedConfig, heightEstimatedConfig);
//        measureChild(widthEstimatedConfig,heightEstimatedConfig);
        return true;
    }

    /**
     * 计算子组件
     *
     * @param widthEstimatedConfig 宽度配置
     * @param heightEstimatedConfig 高度配置
     */
    public void measureChild(int widthEstimatedConfig, int heightEstimatedConfig) {
        int width = EstimateSpec.getSize(widthEstimatedConfig);
        int childWidth = width / mColumnNum;
        for (RecyclerItem item : mRecycler.getAttachedScrap()) {
            if (item.width == 0 && item.child != null) {
                LayoutConfig lc = item.child.getLayoutConfig();
                int childWidthMeasureSpec = EstimateSpec.getChildSizeWithMode(
                        childWidth - lc.getMarginLeft() - lc.getMarginRight(),
                        getEstimatedWidth(), EstimateSpec.PRECISE);
                int childHeightMeasureSpec = EstimateSpec.getChildSizeWithMode(
                        lc.height, getEstimatedHeight(), EstimateSpec.UNCONSTRAINT);
                item.child.estimateSize(childWidthMeasureSpec, childHeightMeasureSpec);
                item.width = childWidth - lc.getMarginLeft() - lc.getMarginRight();
                item.height = item.child.getEstimatedHeight();
                item.marginLeft = lc.getMarginLeft();
                item.marginTop = lc.getMarginTop();
                item.marginRight = lc.getMarginRight();
                item.marginBottom = lc.getMarginBottom();
            }
        }
    }

    @Override
    public boolean onArrange(int left, int top, int width, int height) {
        // 第一次fill，从item0开始一直到leftHeight和rightHeight都大于height为止。
        if (mRecycler.getAttachedScrap().isEmpty()) {
            mLayoutManager.fill(left, top, left + width, top + height, DIRECTION_UP);
        }

        for (RecyclerItem item : mRecycler.getAttachedScrap()) {
            item.child.arrange(item.positionX + item.marginLeft,
                    scrollY + item.positionY + item.marginTop, item.width, item.height);
        }
        return true;
    }

    @Override
    public boolean canAcceptScroll(Component component, int index, boolean b) {
        return false;
    }

    @Override
    public void onContentScrolled(Component component, int index, int i1, int i2, int i3) {
    }

    @Override
    public void scrolledStageUpdate(Component component, int newStage) {
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        return true;
    }

    @Override
    public void onDragDown(Component component, DragInfo dragInfo) {
    }


    @Override
    public void onDragStart(Component component, DragInfo dragInfo) {
        startY = dragInfo.startPoint.getPointYToInt();
    }

    @Override
    public void onDragUpdate(Component component, DragInfo dragInfo) {
        int dt = dragInfo.updatePoint.getPointYToInt() - startY;
        int tryScrollY = dt + scrollY;
        startY = dragInfo.updatePoint.getPointYToInt();
        mDirection = dt < 0 ? DIRECTION_UP : DIRECTION_DOWN;
        mChange = mLayoutManager.fill(0, -tryScrollY, getEstimatedWidth(), -tryScrollY + getEstimatedHeight(), mDirection);
        if (mChange) {
            scrollY = tryScrollY;
            postLayout();
        }
    }

    @Override
    public boolean onDrag(Component component, DragEvent event) {
        return super.onDrag(component, event);
    }

    @Override
    public void onDragEnd(Component component, DragInfo dragInfo) {
    }

    @Override
    public void onDragCancel(Component component, DragInfo dragInfo) {
    }

    @Override
    public boolean onDragPreAccept(Component component, int dragDirection) {
        return true;
    }
}
